/*
 * Copyright (C) 2009  Sylvain Maucourt
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307
 *
 */
package android.fwirk.facebook;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.security.NoSuchAlgorithmException;
import java.util.Date;
import java.util.List;

import org.apache.entity.mime.content.ByteBody;
import org.apache.http.HttpResponse;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.entity.mime.HttpMultipartMode;
import org.apache.http.entity.mime.MultipartEntity;
import org.apache.http.entity.mime.content.StringBody;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.protocol.HTTP;
import org.apache.http.util.EntityUtils;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;


import android.content.Context;
import android.content.SharedPreferences;
import android.text.Html;
import android.util.Log;

public class FacebookClient {

	static final String PREF = "faceplurktweet.where";
	static final String PREF_SESSION = "faceplurktweet.where.facebook.session";
	static final String PREF_SECRET = "faceplurktweet.where.facebook.secret";
	static final String PREF_UID = "faceplurktweet.where.facebook.uid";
	public static final String SECRET = "3bce3c6cb3f7e50386c134369e030ef3";
	public static final String API_KEY = "fc5b7d67aad82497abdcbb1c8d89ccac";
	private static final String v = "1.0";
	public static final String URL_LOGSUCCESS ="http://loginsuccess";
	public static final String URL_LOGFAILURE ="http://loginfailure";
	public static final String URL_PERMSUCCESS ="http://permsuccess";
	public static final String URL_PERMFAILURE ="http://permfailure";
	public static final String URL_PERM = "http://www.facebook.com/connect/prompt_permissions.php?api_key=fc5b7d67aad82497abdcbb1c8d89ccac&ext_perm=read_stream,publish_stream,offline_access&next="+URL_PERMSUCCESS+"&cancel="+URL_PERMFAILURE+"&display=wap";
	public static final String URL_AUTH = "http://m.facebook.com/tos.php?api_key="+API_KEY+"&v=1.0&next="+URL_LOGSUCCESS+"&cancel="+URL_LOGFAILURE;
	public static final String URL_LOGOUT = "http://m.facebook.com/logout.php";
	
	private static final String format = "JSON";
	private static final String server = "http://api.facebook.com/restserver.php";
	
	public static final String PERM_READ = "read_stream";
	public static final String PERM_PUBLISH = "publish_stream";
	
	String secretGenerated;
	String uid;
	String session;
	String name;
	String status;
	boolean permitted;
	boolean loggedIn;
	
	FacebookUser fUser;
	
	private SharedPreferences pref;
	private SharedPreferences.Editor editor;

	public FacebookClient(Context context) {
		permitted=false;
		pref = context.getSharedPreferences(PREF, Context.MODE_PRIVATE);
		editor = pref.edit();
		initialize();
	}

	public void initialize() {
		session = pref.getString(PREF_SESSION, null);
		uid = pref.getString(PREF_UID, null);
		secretGenerated = pref.getString(PREF_SECRET, null);
	}

	public boolean initialized() {
		return secretGenerated != null && uid != null && session != null;
	}

	private static final String getSig(StringBuilder params, String s)
			throws NoSuchAlgorithmException, UnsupportedEncodingException {
		return SimpleMD5.MD5(params.toString().replaceAll("&", "") + s);
	}

	public static final String createToken() throws ClientProtocolException,
			IOException, NoSuchAlgorithmException {
		StringBuilder params = new StringBuilder();
		params.append("api_key=" + API_KEY);
		params.append("&format=" + format);
		params.append("&method=Auth.createToken");
		params.append("&v=" + v);
		params.append("&sig=" + getSig(params, SECRET));
		Log.d("Test createtoken",params.toString());
		return postRequest(params.toString());
	}

	public void getSession(String token) throws ClientProtocolException,
			IOException, NoSuchAlgorithmException, JSONException {
		StringBuilder params = new StringBuilder();
		params.append("api_key=" + API_KEY);
		params.append("&auth_token=" + token);
		params.append("&format=" + format);
		params.append("&generate_session_secret=true");
		params.append("&method=Auth.getSession");
		params.append("&v=" + v);
		params.append("&sig=" + getSig(params, SECRET));
		Log.d("Test getsession",params.toString());
		String result = postRequest(params.toString());
		JSONObject json = new JSONObject(result);

		String error = json.optString("error_code", null);
		if (error != null) {
			throw new IOException(json.getString("error_msg"));
		}
		Log.d("Test",result);
		secretGenerated = json.getString("secret");
		uid = json.getString("uid");
		session = json.getString("session_key");
		Log.d("Test secret",secretGenerated);
		Log.d("Test uid",uid);
		Log.d("Test session",session);
		
		commit();
	}

	public void commit() {
		editor.putString(PREF_SESSION, session);
		editor.putString(PREF_SECRET, secretGenerated);
		editor.putString(PREF_UID, uid);
		editor.commit();
	}

	public void reset() {
		session = null;
		secretGenerated = null;
		uid = null;
		editor.remove(PREF_SESSION);
		editor.remove(PREF_SECRET);
		editor.remove(PREF_UID);
		editor.commit();
	}

	public synchronized void initCurrentUser() throws ClientProtocolException,
			IOException, NoSuchAlgorithmException, JSONException {
		List<FacebookUser> user = getUserInfo(uid);
		name = Html.fromHtml(user.get(0).name).toString();
		status = Html.fromHtml(user.get(0).status).toString();
		fUser=user.get(0);
		fUser.setId(uid);
	}

	public boolean isAppUser() throws ClientProtocolException, IOException,
			NoSuchAlgorithmException {
		StringBuilder params = new StringBuilder();
		params.append("api_key=" + API_KEY);
		params.append("&call_id=" + System.currentTimeMillis());
		params.append("&format=" + format);
		params.append("&method=Users.isAppUser");
		params.append("&session_key=" + session);
		params.append("&v=" + v);
		params.append("&sig=" + getSig(params, secretGenerated));
		//params.append("&sig=" + getSig(params, SECRET));
		
		Log.d("Test isappuser",params.toString());
		String result = postRequest(params.toString());
		Log.d("Test", result);
		return Boolean.parseBoolean(result);
	}

	/**
	 * You need to have right:
	 * http://wiki.developers.facebook.com/index.php/Extended_permission
	 * 
	 * @param status
	 * @return
	 * @throws ClientProtocolException
	 * @throws IOException
	 * @throws NoSuchAlgorithmException
	 * @throws JSONException
	 */
	public boolean setStatus(String status) throws ClientProtocolException,
			IOException, NoSuchAlgorithmException, JSONException {
		StringBuilder params = new StringBuilder();
		params.append("api_key=" + API_KEY);
		params.append("&call_id=" + System.currentTimeMillis());
		params.append("&format=" + format);
		params.append("&method=Status.set");
		params.append("&session_key=" + session);
		params.append("&status=" + status);
		params.append("&v=" + v);
		params.append("&sig=" + getSig(params, secretGenerated));
		//params.append("&sig=" + getSig(params, SECRET));
		Log.d("Test setstatus",params.toString());
		String result = postRequest(params.toString());
		boolean b = Boolean.parseBoolean(result);
		if (!b) {
			JSONObject o = new JSONObject(result);
			throw new IOException(o.getString("error_msg"));
		}
		return b;
	}

	public void notify(String notification) throws ClientProtocolException,
			IOException, NoSuchAlgorithmException, JSONException {
		notify(notification, "");
	}

	public void notify(String notification, long[] uids)
			throws ClientProtocolException, IOException,
			NoSuchAlgorithmException, JSONException {
		StringBuilder strUids = new StringBuilder("[");

		for (int i = 0; i < uids.length; i++) {
			strUids.append(uids[i]);
			if (i + 1 < uids.length) {
				strUids.append(",");
			}
		}

		strUids.append("]");

		notify(notification, strUids.toString());
	}

	public void notify(String notification, String uids)
			throws ClientProtocolException, IOException,
			NoSuchAlgorithmException, JSONException {
		StringBuilder params = new StringBuilder();
		params.append("api_key=" + API_KEY);
		params.append("&call_id=" + System.currentTimeMillis());
		params.append("&format=" + format);
		params.append("&method=Notifications.send");
		params.append("&notification=" + notification);
		params.append("&session_key=" + session);
		params.append("&to_ids=" + uids);
		params.append("&v=" + v);
		params.append("&sig=" + getSig(params, secretGenerated));
		//params.append("&sig=" + getSig(params, SECRET));
		Log.d("Test notify",params.toString());
		String result = postRequest(params.toString());
		if (result.contains("error_msg")) {
			JSONObject o = new JSONObject(result);
			throw new IOException(o.getString("error_msg"));
		}
	}
	
	public boolean checkPermission(String permission) throws ClientProtocolException,
			IOException, NoSuchAlgorithmException, JSONException {
		StringBuilder params = new StringBuilder();
		params.append("api_key=" + API_KEY);
		params.append("&call_id=" + System.currentTimeMillis());
		params.append("&ext_perm=" + permission);
		params.append("&format=" + format);
		params.append("&method=Users.hasAppPermission");
		params.append("&session_key=" + session);
		params.append("&uid=" + uid);
		params.append("&v=" + v);
		//params.append("&sig=" + getSig(params, secretGenerated));
		params.append("&sig=" + getSig(params, secretGenerated));
		
		Log.d("Test check perm",params.toString());
		String result = postRequest(params.toString());
		Log.d("Test", result);
		int b = Integer.parseInt(result);
		if(b==1) {
			return true;
		}
		return false;
	}

	public JSONArray getStatus(String uid, int limit)
			throws ClientProtocolException, IOException,
			NoSuchAlgorithmException, JSONException {
		StringBuilder params = new StringBuilder();
		params.append("api_key=" + API_KEY);
		params.append("&call_id=" + System.currentTimeMillis());
		params.append("&format=" + format);
		params.append("&limit=" + limit);
		params.append("&method=Status.get");
		params.append("&session_key=" + session);
		params.append("&uid=" + uid);
		params.append("&v=" + v);
		params.append("&sig=" + getSig(params, secretGenerated));
		//params.append("&sig=" + getSig(params, SECRET));
		
		Log.d("Test get status",params.toString());
		String result = postRequest(params.toString());
		Log.d("Test get status",result);
		JSONArray array = new JSONArray(result);
		return array;
	}
	
	public JSONArray getWall(String user_id, Date offset, int limit)
		throws ClientProtocolException, IOException,
		NoSuchAlgorithmException, JSONException {
		StringBuilder params = new StringBuilder();
		params.append("api_key=" + API_KEY);
		params.append("&call_id=" + System.currentTimeMillis());
		params.append("&format=" + format);
		params.append("&method=Fql.multiquery");
		String query="{\"posts\":\"SELECT post_id,message,source_id,actor_id,updated_time,created_time,attribution,attachment,comments FROM stream WHERE viewer_id="+uid;
		if(offset!=null) {
			query=query+" AND updated_time < "+offset.getTime()/1000;
		}
		query=query+" limit "+limit+"\", \"profiles\":\"SELECT id,name,url,pic FROM profile WHERE id IN (SELECT actor_id FROM #posts)\"}";
		params.append("&queries=" + query);
		params.append("&session_key=" + session);
		params.append("&v=" + v);
		params.append("&sig=" + getSig(params, secretGenerated));
		//params.append("&sig=" + getSig(params, SECRET));
		
		Log.d("Test get wall",params.toString());
		String result = postRequest(params.toString());
		Log.d("Test get wall",result);
		JSONArray array = new JSONArray(result);
		return array;
	}
	
	public JSONArray getComments(String post_id)
		throws ClientProtocolException, IOException,
		NoSuchAlgorithmException, JSONException {
		StringBuilder params = new StringBuilder();
		params.append("api_key=" + API_KEY);
		params.append("&call_id=" + System.currentTimeMillis());
		params.append("&format=" + format);
		params.append("&method=Fql.multiquery");
		String query="{\"comments\":\"SELECT post_id, fromid, time, text FROM comment WHERE post_id='"+post_id+"' limit 30\", \"profiles\":\"SELECT id,name,url,pic FROM profile WHERE id IN (SELECT fromid FROM #comments)\"}";
		params.append("&queries=" + query);
		params.append("&session_key=" + session);
		params.append("&v=" + v);
		params.append("&sig=" + getSig(params, secretGenerated));
		//params.append("&sig=" + getSig(params, SECRET));
		
		Log.d("Test get comments",params.toString());
		String result = postRequest(params.toString());
		Log.d("Test get comments",result);
		JSONArray array = new JSONArray(result);
		return array;
	}
	
	public boolean addComment(String post_id,String message)
	throws ClientProtocolException, IOException,
		NoSuchAlgorithmException, JSONException {
		StringBuilder params = new StringBuilder();
		params.append("api_key=" + API_KEY);
		params.append("&call_id=" + System.currentTimeMillis());
		params.append("&comment=" + message);
		params.append("&format=" + format);
		params.append("&method=Stream.addComment");
		params.append("&post_id="+post_id);
		params.append("&session_key=" + session);
		params.append("&v=" + v);
		params.append("&sig=" + getSig(params, secretGenerated));
		//params.append("&sig=" + getSig(params, SECRET));
		
		Log.d("Test add comment",params.toString());
		String result = postRequest(params.toString());
		Log.d("Test add comment",result);
		try {
			JSONObject res = new JSONObject(result);
			String er=res.optString("error_code","");
			if(er.equals("")) {
				return true;
			}
		}
		catch(Exception e) {
			e.printStackTrace();
			if(e instanceof JSONException) {
				return true;
			}
		}
		return false;
	}
	
	public boolean removePost(String post_id)
	throws ClientProtocolException, IOException,
		NoSuchAlgorithmException, JSONException {
		StringBuilder params = new StringBuilder();
		params.append("api_key=" + API_KEY);
		params.append("&call_id=" + System.currentTimeMillis());
		params.append("&format=" + format);
		params.append("&method=Stream.remove");
		params.append("&post_id="+post_id);
		params.append("&session_key=" + session);
		params.append("&v=" + v);
		params.append("&sig=" + getSig(params, secretGenerated));
		//params.append("&sig=" + getSig(params, SECRET));
		Log.d("Test remove post",params.toString());
		String result = postRequest(params.toString());
		Log.d("Test remove post",result);
		boolean b = Boolean.parseBoolean(result);
		if (!b) {
			JSONObject o = new JSONObject(result);
			throw new IOException(o.getString("error_msg"));
		}
		return b;
	}
	
	public JSONObject getStream(String uid,Date offset, int limit)
		throws ClientProtocolException, IOException,
		NoSuchAlgorithmException, JSONException {
		StringBuilder params = new StringBuilder();
		params.append("api_key=" + API_KEY);
		params.append("&call_id=" + System.currentTimeMillis());
		if(offset!=null) {
			params.append("&end_time=" + offset.getTime()/1000);
		}
		params.append("&format=" + format);
		params.append("&limit=" + limit);
		params.append("&method=Stream.get");
		params.append("&session_key=" + session);
		params.append("&start_time=0");
		params.append("&v=" + v);
		params.append("&viewer_id=" + uid);
		params.append("&sig=" + getSig(params, secretGenerated));
		//params.append("&sig=" + getSig(params, SECRET));
		
		Log.d("Test get stream",params.toString());
		String result = postRequest(params.toString());
		Log.d("Test get stream",result);
		JSONObject res = new JSONObject(result);
		return res;
	}

	public long[] getFriends() throws ClientProtocolException, IOException,
			NoSuchAlgorithmException, JSONException {
		StringBuilder params = new StringBuilder();
		params.append("api_key=" + API_KEY);
		params.append("&call_id=" + System.currentTimeMillis());
		params.append("&format=" + format);
		params.append("&method=Friends.get");
		params.append("&session_key=" + session);
		params.append("&v=" + v);
		params.append("&sig=" + getSig(params, secretGenerated));
		//params.append("&sig=" + getSig(params, SECRET));
		Log.d("Test getfriends",params.toString());
		String result = postRequest(params.toString());
		JSONArray json = new JSONArray(result);
		long[] friends = new long[json.length()];

		for (int i = 0; i < json.length(); i++) {
			friends[i] = json.getInt(i);
		}

		return friends;
	}

	
	private static String postRequest(String params)
			throws ClientProtocolException, IOException {
		return postRequest(params, "application/x-www-form-urlencoded");
	}

	private static String postRequest(String params, String contentType)
			throws ClientProtocolException, IOException {
		HttpClient client = new DefaultHttpClient();
		HttpPost post = new HttpPost(server);
		post.setHeader(HTTP.CONTENT_TYPE, contentType);
		post.setEntity(new StringEntity(params));
		HttpResponse response = client.execute(post);
		if (response.getStatusLine().getStatusCode() == 200) {
			return EntityUtils.toString(response.getEntity());
		}

		throw new IOException(response.getStatusLine().getReasonPhrase());
	}

	public List<FacebookUser> getCurrentUserInfo() throws ClientProtocolException,
			IOException, NoSuchAlgorithmException, JSONException {
		return getUserInfo(uid);
	}

	public List<FacebookUser> getUserInfo(String uids) throws ClientProtocolException,
			IOException, NoSuchAlgorithmException, JSONException {
		String fields = "about_me," + "activities," + "proxied_email,"
				+ "interests," + "meeting_for," + "meeting_sex," + "name,"
				+ "pic," + "profile_url," + "relationship_status," + "sex,"
				+ "status";
		StringBuilder params = new StringBuilder();
		params.append("api_key=" + API_KEY);
		params.append("&call_id=" + System.currentTimeMillis());
		params.append("&fields=" + fields);
		params.append("&format=" + format);
		params.append("&method=Users.getInfo");
		params.append("&session_key=" + session);
		params.append("&uids=" + uids);
		params.append("&v=" + v);
		params.append("&sig=" + getSig(params, secretGenerated));
		//params.append("&sig=" + getSig(params, SECRET));
		Log.d("Test getinfo",params.toString());
		String result = postRequest(params.toString());
		Log.d("Test getinfo",result);
		JSONArray users = new JSONArray(result);

		List<FacebookUser> r=FacebookUser.getFacebookUsers(users);
		return r;

	}

	/**
	 * You need to have right:
	 * http://wiki.developers.facebook.com/index.php/Extended_permission
	 * 
	 * @param picture
	 * @param caption
	 * @throws NoSuchAlgorithmException
	 * @throws ClientProtocolException
	 * @throws IOException
	 * @throws JSONException
	 */
	public void upload(byte[] picture, String caption)
			throws NoSuchAlgorithmException, ClientProtocolException,
			IOException, JSONException {
		StringBuilder toSig = new StringBuilder();
		toSig.append("api_key=" + API_KEY);
		long call_id = System.currentTimeMillis();
		toSig.append("&call_id=" + Long.toString(call_id));
		toSig.append("&caption=" + caption);
		toSig.append("&format=" + format);
		toSig.append("&method=Photos.upload");
		toSig.append("&session_key=" + session);
		toSig.append("&v=" + v);
		String sig = getSig(toSig, secretGenerated);
		//String sig = getSig(toSig, SECRET);
		MultipartEntity multi = new MultipartEntity(HttpMultipartMode.STRICT);
		ByteBody photo = new ByteBody(picture);
		multi.addPart("api_key", new StringBody(API_KEY));
		multi.addPart("call_id", new StringBody(Long.toString(call_id)));
		multi.addPart("caption", new StringBody(caption));
		multi.addPart("format", new StringBody(format));
		multi.addPart("method", new StringBody("Photos.upload"));
		multi.addPart("session_key", new StringBody(session));
		multi.addPart("sig", new StringBody(sig));
		multi.addPart("v", new StringBody(v));
		multi.addPart("photo", photo);

		HttpClient client = new DefaultHttpClient();
		HttpPost post = new HttpPost(server);
		post.setEntity(multi);

		String result = EntityUtils.toString(client.execute(post).getEntity());
		JSONObject json = new JSONObject(result);

		String error = json.optString("error_code", null);
		if (error != null) {
			throw new IOException(json.getString("error_msg"));
		}
	}

	public String getUid() {
		return uid;
	}

	public void setUid(String uid) {
		this.uid = uid;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public String getStatus() {
		return status;
	}

	public FacebookUser getfUser() {
		return fUser;
	}

	public void setfUser(FacebookUser fUser) {
		this.fUser = fUser;
	}

	public boolean isPermitted() {
		return permitted;
	}

	public void setPermitted(boolean permitted) {
		this.permitted = permitted;
	}

	public boolean isLoggedIn() {
		return loggedIn;
	}

	public void setLoggedIn(boolean loggedIn) {
		this.loggedIn = loggedIn;
	}

	

}